//http 响应
use std::io::{Read,Write};
use std::collections::HashMap;

#[derive(Debug,PartialEq,Clone)]   //需要实现的trait
//自己不实现 编译器会默认帮忙实现这些 trait
//结构体成员涉及 引用类型，就要给定声明周期
pub struct HttpResponse<'a>{
    version:&'a str,
    status_code:&'a str,
    status_text:&'a str,
    headers:Option<HashMap<&'a str,&'a str>>,
    //body 是对象自己持有的类型 其他的成员都是引用类型 自己不持有
    body:Option<String>//option 包裹说明此字段可能为空 需要判断一下 再去使用
}
//有了此trait 可以为new出来的对象 某些字段指定默认值
impl<'a> Default for HttpResponse<'a>{
    fn default() -> Self {
        Self{
            version:"HTTP/1.1".into(),
            status_code:"200".into(),
            status_text:"OK".into(),
            headers:None,
            body:None,
        }
    }
}

//实现from trait 将结构体转成字符串
impl<'a> From<HttpResponse<'a>> for String{
    fn from(res:HttpResponse)->String{
        let res1=res.clone();
        format!("{} {} {}\r\n{}Content-Length:{}\r\n\r\n{}",
            &res1.version,
            &res1.status_code,
            &res1.status_text,
            &res1.headers(),
            //直接调用 .body body若为空，会panic
            //这里怎么解决 就不panic呢 OK的做法
            match res.body{
                Some(h)=>h.len(),
                None=>0,
            },
            //&res.body.unwrap().len(),//unwrap会获取res1的所有权 所以这里用res 获取res的所有权 res1还能用
            &res1.body()
        )
    }
}

//关联的new 调用的时候 HttpResponse::new 调用
impl<'a> HttpResponse<'a>{
    pub fn new(
        status_code:&'a str,
        headers:Option<HashMap<&'a str,&'a str>>,
        body:Option<String>
    )->HttpResponse<'a>{
        //mut的response 才能对其成员赋值
        let mut response=HttpResponse::default();
        if status_code!="200"{
            response.status_code=status_code.into();
        }
        //传入的headers 可能为空 需要match处理
        //若不为空直接赋值
        //若为空 自己构造默认的hashmap值 给response.headers 赋值
        response.headers=match  &headers {
            //也可以返回Some(_h) 不能返回 _h 因为_h 不是option 类型了
            Some(_h)=>headers,
            None=>{
                let mut h=HashMap::new();
                h.insert("Content-Type","text/html");
                Some(h)//因为response.headers 本身是Option类型，所以返回时需要Some 包裹
            },
        };

        response.status_text=match response.status_code{
            "200"=>"OK".into(),
            "400"=>"Bad Req".into(),
            "404"=>"Not Found".into(),
            "500"=>"Internal Server Err".into(),
            _=>"Not Found".into(),
        };

        response.body=body;//body若为空就给.body 赋值空
        response  //返回

    }
    //带self的不是关联函数  而是方法 靠obj.send_response 去调用
    //实现了 Write trait的对象才能传进来
    //Result 作为返回值 必须有2个参数
    pub fn send_response(&self,write_stream:&mut impl Write)->Result<(),()>{
        let res: HttpResponse<'a>=self.clone();
        //将结构体变量数据 整体转化成 字符串
        //需要实现 HttpResponse 的from trait，才能直接调用此方法去转
        let response_string=String::from(res);
        let _=write!(write_stream,"{}",response_string);
        Ok(())
    }

    //实现 getter 方法
    fn version(&self)->&str{
        self.version
    }
    fn status_code(&self)->&str{
        self.status_code
    }
    fn status_text(&self)->&str{
        self.status_text
    }

    //将所有的键值对 转化成 字符串，中间有换行
    fn headers(&self)->String{
        //不能获取源 self.headers 中的内容的所有权 所以需要 clone unwrap会获取所有权
        let map=self.headers.clone().unwrap();
        let mut header_string="".into();//to_string 行吗
        for (k,v) in map.iter(){
            header_string=format!("{}{}:{}\r\n",header_string,k,v);
        }
        header_string
    }

    fn body(&self)->&str{//返回的切片
        match &self.body {
            Some(b)=>b.as_str(),//b是String as_str 转成切片
            None=>"",
        }
    }
}

mod tests{
    use crate::httprequest::HttpRequest;

    use super::*;
    #[test]
    fn test_response_struct_creation_200(){
        let response_actual=HttpResponse::new(
            "200",
            None,
            Some("xxx".into()),
        );

        let response_expect=HttpResponse{
            version:"HTTP/1.1",
            status_code:"200",
            status_text:"OK",
            headers:{
                let mut h=HashMap::new();
                h.insert("Content-Type", "text/html");
                Some(h)
            },
            body:Some("xxx".into()),  
        };
        assert_eq!(response_actual,response_expect);
    }

    #[test]
    fn test_response_struct_creation_404(){
        let response_actual=HttpResponse::new(
            "404",
            None,
            Some("xxx".into()),
        );

        let response_expect=HttpResponse{
            version:"HTTP/1.1",
            status_code:"404",
            status_text:"Not Found",
            headers:{
                let mut h=HashMap::new();
                h.insert("Content-Type", "text/html");
                Some(h)
            },
            body:Some("xxx".into()),  
        };
        assert_eq!(response_actual,response_expect);
    }

    #[test]
    fn test_http_response_creation(){
        let response_expected=HttpResponse{
            version:"HTTP/1.1",
            status_code:"404",
            status_text:"Not Found",
            headers:{
                let mut h=HashMap::new();
                h.insert("Content-Type", "text/html");
                Some(h)
            },
            body:Some("xxx".into()),
        };
        let http_string=String::from(response_expected);//不需要注明类型 String
        //let http_string:String=response_expected.into();//必须注明类型 否则into失败
        let actual_string="HTTP/1.1 404 Not Found\r\nContent-Type:text/html\r\nContent-Length:3\r\n\r\nxxx";
        assert_eq!(http_string,actual_string);

    }
}